﻿using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;
using winS.Unity.Process;
using winS.UnityEditor.UIElement;
using static winS.Unity.Process.RouletteRandomStep;
using Button = winS.UnityEditor.UIElement.Button;
using FloatField = winS.UnityEditor.UIElement.FloatField;
using Label = winS.UnityEditor.UIElement.Label;

namespace winS.UnityEditor.ProcessEditor
{
    [Title("轮盘随机", "Roulette Random"), StepNodeSearchTreeGroup("内置/轮盘随机")]
    public class RouletteRandomStepNode : DivergenceStepNode<RouletteRandomStep>
    {
        private VisualElement betControlContainer;

        private FloatField totalWeightField;
        private readonly List<BetControl> betControlList = new List<BetControl>();

        protected internal override void OnCreated()
        {
            portContainer.style.alignItems = Align.Center;
            portContainer.Add(totalWeightField = EditorElementUtility.elementFactory.CreateFloatField("总权重:"));
            totalWeightField.inputField.SetEnabled(false);
            totalWeightField.inputElement.style.SetFixedWidth(40f);
            portContainer.style.justifyContent = Justify.SpaceBetween;
            Add(betControlContainer = EditorElementUtility.elementFactory.CreateEditorElement());
            betControlContainer.style.marginTop = 2f;
            Button button = EditorElementUtility.elementFactory.CreateButton(PackageAssets.Icons.add, 12f, ColorLevelUtility.GetColor(19), () =>
            {
                Bet newBet = new Bet();
                step.betList.Add(newBet);
                CreateBetControl(newBet);
                CalculateTotalWeight();
            }, 28f);
            button.style.SetHorizontalMargin(0f);
            button.SetColor(headColor);
            button.style.SetBorderBottomRadius(borderRadius);
            Add(button);
            foreach (var bet in step.betList)
            {
                CreateBetControl(bet);
            }
        }
        public override IEnumerable<OutputPort> GetOutputPorts()
        {
            return betControlList.Select(betControl => betControl.outputPort);
        }

        protected internal override void OnReady()
        {
            CalculateTotalWeight();
        }

        private void CalculateTotalWeight()
        {
            float totalWeight = betControlList.Select(betControl => betControl.weight).Sum();
            totalWeightField.SetValueWithoutNotify(totalWeight);
            betControlList.ForEach(betControl => betControl.UpdateProbability(totalWeight));
        }
        private BetControl CreateBetControl(Bet logicBranchEntry)
        {
            BetControl betControl = new BetControl(logicBranchEntry, this);
            betControl.onDelete += () =>
            {
                foreach (var connectionline in betControl.outputPort.connectionlines.ToList())
                {
                    connectionline.Disconnect();
                    stepGraphView.RemoveConnectionline(connectionline);
                }
                betControlList.Remove(betControl);
                betControlContainer.Remove(betControl);
                step.betList.Remove(logicBranchEntry);
            };
            betControl.onWeightChanged += CalculateTotalWeight;
            betControlList.Add(betControl);
            betControlContainer.Add(betControl);
            return betControl;
        }

        public class BetControl : VisualElement
        {
            public event Action onDelete;
            public event Action onWeightChanged;

            public float weight => bet.weight;

            public OutputPort outputPort { get; private set; }

            private readonly Bet bet;
            private readonly ProbabilityBar probabilityBar;

            public BetControl(Bet bet, StepNode stepNode)
            {
                this.bet = bet;
                style.flexDirection = FlexDirection.Row;
                style.SetBorderRadius(2.4f);
                style.SetBackgroundColor(ColorLevelUtility.GetColor(6));
                style.marginTop = style.marginBottom = 2f;

                Button deleteButton = EditorElementUtility.elementFactory.CreateButton(PackageAssets.Icons.error, 12f, ColorLevelUtility.GetColor(19), () => onDelete?.Invoke());
                deleteButton.style.SetBorderLeftRadius(2.4f);
                deleteButton.SetColor(new Color32(99, 39, 39, 255));
                Add(deleteButton);

                FloatField weightField = stepNode.guiFactory.CreateFloatField("权重:", bet.weight, newValue =>
                {
                    bet.weight = newValue;
                    onWeightChanged?.Invoke();
                });
                weightField.inputElement.style.SetMinWidth(40f);
                Add(weightField);

                Add(probabilityBar = new ProbabilityBar());

                Add(outputPort = new OutputPort(stepNode, bet.nextList));
                outputPort.style.alignSelf = Align.Center;
                outputPort.style.marginLeft = 4f;
                outputPort.onConnected += OnConnected;
                outputPort.onDisconnected += OnDisconnected;
            }

            public void UpdateProbability(float totalWeight)
            {
                probabilityBar.SetValue(weight / totalWeight);
            }
            private void OnConnected(Port inputPort)
            {
                bet.AddStep(inputPort.node.step);
            }
            private void OnDisconnected(Port inputPort)
            {
                bet.Remove(inputPort.node.step);
            }

            public class ProbabilityBar : VisualElement
            {
                private readonly Label percentLabel;
                private readonly VisualElement bar;
                private readonly VisualElement barBackground;

                public ProbabilityBar()
                {
                    style.flexGrow = 1f;
                    style.SetMaxHeight(20f);
                    Add(percentLabel = EditorElementUtility.elementFactory.CreateLabel(9f));
                    Add(barBackground = EditorElementUtility.elementFactory.CreateRow(1f));
                    barBackground.style.SetBackgroundColor(ColorLevelUtility.GetColor(3));

                    //style.SetFixedHeight(20f);
                    //style.SetFixedWidth(80f);

                    barBackground.Add(bar = EditorElementUtility.elementFactory.CreateRow());
                    bar.style.SetTransition("width", 0.2f);
                    bar.style.SetBackgroundColor(new Color32(0, 230, 0, 255));
                    bar.style.SetWidth(50f, LengthUnit.Percent);
                }

                public void SetValue(float ratio)
                {
                    percentLabel.text = float.IsNaN(ratio) ? "0" : $"{Math.Round(ratio * 100f)}%";
                    bar.style.SetWidth(ratio * 100f, LengthUnit.Percent);
                }
            }
        }
    }
}